package cn.alex.sso.framework.aspect;

import cn.alex.sso.framework.annotations.OperatLog;
import cn.alex.sso.framework.util.BrowserUtil;
import cn.alex.sso.project.entity.SysOperateLog;
import cn.alex.sso.project.entity.SysUser;
import cn.alex.sso.project.service.SysOperateLogService;
import cn.alex.sso.project.service.SysUserService;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.JSONUtil;
import com.jthinking.common.util.ip.IPInfoUtils;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 操作日志
 *
 * @author Alex
 * @date 2024/10/22 上午10:19
 */
@Aspect
@Component
@RequiredArgsConstructor
public class OperatLogAspect {
    private final SysOperateLogService operateLogService;
    private final SysUserService userService;

    @Around("@annotation(cn.alex.sso.framework.annotations.OperatLog)")
    public Object doAfterReturning(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest req = attributes.getRequest();

        // 判断当前接口的执行方法是否添加记录操作日志注解
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        OperatLog annotation = AnnotationUtil.getAnnotation(method, OperatLog.class);
        if (ObjUtil.isEmpty(annotation)) {
            return joinPoint.proceed();
        }

        // 记录执行时间
        TimeInterval timer = DateUtil.timer();

        // 执行方法
        Object result = joinPoint.proceed();


        SysUser user = new SysUser();
        user.setUsername("-");
        user.setNickName("-");
        long userId = StpUtil.isLogin() ? Long.parseLong((String) StpUtil.getLoginId()) : -1L;
        if (userId != -1L) {
            user = userService.getInfo(userId);
        }

        SysOperateLog operateLog = new SysOperateLog();
        // 用户ID
        operateLog.setUserId(userId);

        // 用户账号
        operateLog.setUsername(user.getUsername());

        // 用户姓名
        operateLog.setNickName(user.getNickName());

        // 操作模块
        operateLog.setModule(annotation.module());

        // 操作动作
        operateLog.setAction(annotation.action());

        // 请求地址
        operateLog.setRequestUrl(req.getRequestURI());

        // 请求方式
        operateLog.setRequestMethod(req.getMethod());

        // 请求数据
        if (ObjUtil.isNotEmpty(joinPoint.getArgs())) {
            if (joinPoint.getArgs()[0] instanceof MultipartFile) {
                operateLog.setRequestData(((MultipartFile) joinPoint.getArgs()[0]).getOriginalFilename());
            } else {
                if (joinPoint.getArgs().length == 1) {
                    operateLog.setRequestData(JSONUtil.toJsonStr(joinPoint.getArgs()[0]));
                } else {
                    operateLog.setRequestData(JSONUtil.toJsonStr(joinPoint.getArgs()));
                }
            }
        }

        // 响应数据
        operateLog.setResponseData(JSONUtil.toJsonStr(result));

        // 执行时间
        operateLog.setExecuteTime(timer.interval());

        // IP地址
        operateLog.setIpAddress(BrowserUtil.getIpAddress(req));

        // 地理位置
        operateLog.setGeoLocation(IPInfoUtils.getIpInfo(operateLog.getIpAddress()).getAddress());

        // 设备信息
        operateLog.setDeviceInfo(BrowserUtil.getOs(req));

        // 浏览器信息
        operateLog.setBrowserInfo(BrowserUtil.getBrowser(req));

        // 操作时间
        operateLog.setOperateTime(new Date());

        ThreadUtil.execute(() -> {
            operateLogService.save(operateLog);
        });

        return result;
    }
}
